package web

import (
	"basic-go/syntax/webook/internal/domain"
	"basic-go/syntax/webook/internal/service"
	"fmt"
	regexp "github.com/dlclark/regexp2"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"go.opentelemetry.io/otel/trace"
	"log"
	"net/http"
	"strings"
)

//import "github.com/gin-gonic/gin"

const (
	emailRegexPattern    = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"
	passwordRegexPattern = `^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$`
	birthdayRegexPattern = `^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$`
	bizLogin             = "login"
)

var JWTKey = []byte("k6CswdUm77WKcbM68UQUuxVsHSpTCwgK")

type UserHandler struct {
	svc         service.UserService
	codeSvc     service.CodeService
	emailExp    *regexp.Regexp
	passwordExp *regexp.Regexp
	birthdayExp *regexp.Regexp
	JWTHandler
}

func NewUserHandler(svc service.UserService, codeSvc service.CodeService) *UserHandler {
	emailExp := regexp.MustCompile(emailRegexPattern, regexp.None)
	passwordExp := regexp.MustCompile(passwordRegexPattern, regexp.None)
	birthdayExp := regexp.MustCompile(birthdayRegexPattern, regexp.None)
	return &UserHandler{
		svc:         svc,
		codeSvc:     codeSvc,
		emailExp:    emailExp,
		passwordExp: passwordExp,
		birthdayExp: birthdayExp,
	}
}

func (u *UserHandler) RegisterRoutes(server *gin.Engine) {
	ug := server.Group("/users")
	ug.POST("/signup", u.SignUp)
	//ug.POST("/login", u.Login)
	ug.POST("/login", u.LoginJWT)
	ug.POST("/edit", u.Edit)
	ug.GET("/logout", u.Logout)
	//ug.GET("/profile", u.Profile)
	ug.GET("/profile", u.ProfileJWT)

	// 手机验证码登录相关功能
	ug.POST("/login_sms/code/send", u.SendSMSLoginCode)
	ug.POST("/login_sms", u.LoginSMS)
}

func (u *UserHandler) SignUp(ctx *gin.Context) {
	//定义了一个内部结构体，该结构体只能在SignUp这个方法体内使用
	type SignUpReq struct {
		Email           string `json:"email"`
		ConfirmPassword string `json:"confirmPassword"`
		Password        string `json:"password"`
	}

	var req SignUpReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	ok, err := u.emailExp.MatchString(req.Email)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !ok {
		ctx.String(http.StatusOK, "你的邮箱格式不对")
		return
	}

	if req.ConfirmPassword != req.Password {
		ctx.String(http.StatusOK, "两次输入的密码不一致")
		return
	}
	ok, err = u.passwordExp.MatchString(req.Password)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !ok {
		ctx.String(http.StatusOK, "密码必须要大于8位，包含数字、特殊字符")
		return
	}

	err = u.svc.SignUp(ctx.Request.Context(), domain.User{Email: req.Email, Password: req.Password})
	switch err {
	case nil:
		ctx.String(http.StatusOK, "注册成功")
	case service.ErrUserDuplicate:
		span := trace.SpanFromContext(ctx.Request.Context())
		span.AddEvent("邮件冲突")
		ctx.String(http.StatusOK, "邮箱冲突")
	default:
		ctx.String(http.StatusOK, "系统异常")

	}

}

func (u *UserHandler) Login(ctx *gin.Context) {
	type LoginReq struct {
		Email    string `json:"Email"`
		Password string `json:"Password"`
	}

	var req LoginReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	user, err := u.svc.Login(ctx.Request.Context(), req.Email, req.Password)

	switch err {
	case nil:
		// 如果能执行到这一步说明 账号和密码能匹配上
		// 设置session
		sess := sessions.Default(ctx)
		sess.Set("userId", user.Id)
		sess.Options(sessions.Options{
			//Secure:   true,
			//HttpOnly: true,
			// 15分钟   60*15
			MaxAge: 10,
		})
		err = sess.Save()
		if err != nil {
			ctx.String(http.StatusOK, "系统错误，sess没有保存成功")
			return
		}
		ctx.String(http.StatusOK, "登录成功")
	case service.ErrInvalidUserOrPassword:
		// 返回用户不存在或密码错误的信息
		ctx.String(http.StatusOK, "用户名不存在或密码不正确")
	default:
		ctx.String(http.StatusOK, "登录功能的默认系统错误")
	}

}

func (u *UserHandler) LoginJWT(ctx *gin.Context) {

	type LoginReq struct {
		Email    string `grom:"email"`
		Password string `grom:"password"`
	}
	var loginReq LoginReq
	err := ctx.Bind(&loginReq)
	if err != nil {
		ctx.String(http.StatusOK, "LoginJWT中的系统错误")
	}
	user, err := u.svc.Login(ctx, loginReq.Email, loginReq.Password)

	switch err {
	case service.ErrInvalidUserOrPassword:
		ctx.String(http.StatusOK, "用户名或密码错误")
		return
	case nil:
		u.SetJWTToken(ctx, user.Id)
		ctx.String(http.StatusOK, "登录成功")
		return
	default:
		ctx.String(http.StatusOK, "系统错误")
		return
	}

}

func (u *UserHandler) Edit(ctx *gin.Context) {
	type EditReq struct {
		Name     string `json:"Name"`
		Birthday string `json:"Birthday"`
		Email    string `json:"Email"`
		Profile  string `json:"Profile"`
		Password string `json:"Password"`
	}
	var req EditReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	// 昵称长度判断
	if len(req.Name) > 30 || len(req.Name) == 0 {
		ctx.String(http.StatusOK, "昵称长度应1-30个长度之间")
		return
	}
	// 简介长度判断
	if len(req.Profile) > 300 {
		ctx.String(http.StatusOK, "简介长度应控制在300字以内")
		return
	}
	// 生日 判断
	ok, err := u.birthdayExp.MatchString(req.Birthday)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误--生日正则表达匹配失败")
		return
	}
	if !ok {
		ctx.String(http.StatusOK, "请输入正确的生日格式，参考：1990-01-01")
		return
	}

	err = u.svc.Update(ctx, domain.User{
		Name:     req.Name,
		Birthday: req.Birthday,
		Email:    req.Email,
		Profile:  req.Profile,
		Password: req.Password,
	})
	if err != nil {
		ctx.String(http.StatusOK, "系统错误，个人信息更新失败")
		return
	}
	ctx.String(http.StatusOK, "信息修改成功")
	return

}

func (u *UserHandler) Logout(ctx *gin.Context) {
	sess := sessions.Default(ctx)
	sess.Options(sessions.Options{
		MaxAge: -1,
	})
	return
}

// 查询用户信息 返回到前端
func (u *UserHandler) Profile(ctx *gin.Context) {
	type ProfileReq struct {
		Name     string `json:"Name"`
		Birthday string `json:"Birthday"`
		Email    string `json:"Email"`
		Profile  string `json:"Profile"`
		//Password string `json:"Password"`
	}
	// session的方式存储信息，则通过session获取用户id
	sess := sessions.Default(ctx)
	userid := sess.Get("userId")
	uid, _ := userid.(int64)
	//fmt.Println("profile输出通过session获取的userId：%d", userid)

	domainU, _ := u.svc.Profile(ctx, uid)
	fmt.Println(domainU.Id, domainU.Email, domainU.Birthday)
	ctx.String(http.StatusOK, "这是profile")
	return
}

func (u *UserHandler) ProfileJWT(ctx *gin.Context) {
	// 通过token的方式存储信息，则解析token获取userId
	//authCode := ctx.GetHeader("authorization")
	authCode := ctx.GetHeader("Authorization")
	if authCode == "" {
		println("profileJWT 1")
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	sep := strings.SplitN(authCode, " ", 2)
	if len(sep) != 2 {
		println("profileJWT 2")
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	tokenStr := sep[1]
	var uc UserClaims
	token, err := jwt.ParseWithClaims(tokenStr, &uc, func(token *jwt.Token) (interface{}, error) {
		return JWTKey, nil
	})

	if err != nil {
		println("profileJWT 3")
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	if token == nil || !token.Valid {
		println("profileJWT 4")
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	uid := uc.Id
	domainUser, _ := u.svc.Profile(ctx, uid)
	ctx.String(http.StatusOK, "这是profile", domainUser)
	return
}

func (u *UserHandler) SendSMSLoginCode(ctx *gin.Context) {
	type SendSMSReq struct {
		Phone string `json:"Phone"`
	}
	var req SendSMSReq
	if err := ctx.Bind(&req); err != nil {
		return
	}
	if req.Phone == "" {
		// 返回json串 提供给前端去处理
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "请输入手机号码"})
		return
	}
	err := u.codeSvc.Send(ctx, bizLogin, req.Phone)
	switch err {
	case nil:
		ctx.JSON(http.StatusOK, Result{Msg: "发送成功"})
	case service.ErrCodeSendTooMany:
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "短信发送太频繁，请稍后再试"})
	default:
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统错误"})
		log.Println(err)
	}

}

func (u *UserHandler) LoginSMS(ctx *gin.Context) {
	type LoginSmsReq struct {
		Phone     string `json:"Phone"`
		Inputcode string `json:"code"`
	}
	var req LoginSmsReq
	println("经过了LoginSMS")
	if err := ctx.Bind(&req); err != nil {
		return
	}
	ok, err := u.codeSvc.Verify(ctx, bizLogin, req.Phone, req.Inputcode)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统异常",
		})
		return
	}
	if !ok {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "验证码不对，请重新输入"})
		return
	}

	//验证码 验证成功后，则需要再去判断 数据库中是否有该手机号
	// 如果有此手机号就直接登录
	// 如果没有此手机号就 创建新用户，写入数据库，
	domainU, err := u.svc.FindOrCreateByPhone(ctx, req.Phone)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统异常，请重试"})
		return
	}
	// 登录成功 需要再设置session 或者token
	err = u.SetJWTToken(ctx, domainU.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统异常"})
		return
	}
	ctx.JSON(http.StatusOK, Result{Msg: "登录成功"})
}

func (u *UserHandler) LoginSMSV1(ctx *gin.Context) {
	type Req struct {
		Phone     string `json:"phone"`
		InputCode string `json:"Code"`
	}
	var req Req
	err := ctx.Bind(&req)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统错误"})
		return
	}
	ok, err := u.codeSvc.Verify(ctx, bizLogin, req.Phone, req.InputCode)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统异常",
		})
		return
	}
	if !ok {
		ctx.JSON(http.StatusOK, Result{Msg: "验证码不正确，请重新输入"})
		return
	}
	// 验证码正确，就查找数据库是否已有该用户
	domainU, err := u.svc.FindOrCreate(ctx, req.Phone)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统错误"})
		return
	}
	err = u.SetJWTToken(ctx, domainU.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统异常"})
		return
	}
	ctx.JSON(http.StatusOK, Result{Msg: "登录成功"})

}
